package org.lisy.java.io;

import com.jcraft.jsch.*;
import org.apache.commons.lang3.ObjectUtils;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * sftp 文件上传(相对路径)
 *
 * @author lisy
 */
public class SftpFileOperate {

    // 原始文件目录
    private final static String RIGINAL_FILE_DIR = "./deploy/riginal";
    // 原始文件
    private final static String RIGINAL_FILE = "riginal_sftp.txt";
    // 远程目标文件目录(相对)
    private final static String TARGET_FILE_DIR = "sftp/file";
    // 远程目标文件
    private final static String TARGET_FILE = "target.txt";
    // SFTP 服务器地址
    private final static String HOST = "10.20.xxx.xxx";
    // SFTP 端口，默认为 22
    private final static Integer PORT = 22;
    // 用户名
    private final static String USERNAME = "root";
    // 密码
    private final static String PASSWORD = "******";

    public static void main(String[] args) {
        // 可选：私钥文件路径，如果使用密码认证则设为 null
        String privateKeyPath = null;
        // 已知主机文件路径
        String knownHostsPath = null;
        // 文件上传
        uploadFileToSFTP(privateKeyPath, knownHostsPath);
    }

    public static void uploadFileToSFTP(String privateKeyPath, String knownHostsPath) {
        Session session = null;
        ChannelSftp channelSftp = null;
        try {
            // 创建JSch对象
            JSch jsch = new JSch();
            // 如果提供了私钥路径，设置私钥
            if (privateKeyPath != null && !privateKeyPath.isEmpty()) {
                jsch.addIdentity(privateKeyPath);
            }
            // 设置已知主机文件 System.getProperty("user.home") + "/.ssh/known_hosts"
            if (knownHostsPath != null && !knownHostsPath.isEmpty()) {
                jsch.setKnownHosts(knownHostsPath);
            }
            // 创建会话
            session = jsch.getSession(USERNAME, HOST, PORT);
            if (ObjectUtils.isNotEmpty(PASSWORD)) {
                session.setPassword(PASSWORD);
            }
            // 设置不严格检查主机密钥（仅用于测试，生产环境应妥善处理）
            session.setConfig("StrictHostKeyChecking", "no");
            // 连接会话
            session.connect();
            System.out.println("已连接到 SFTP 服务器");
            // 打开SFTP通道
            channelSftp = (ChannelSftp) session.openChannel("sftp");
            channelSftp.connect();
            System.out.println("已建立 SFTP 通道");

            // 确保远程目录存在
            try {
                channelSftp.cd(TARGET_FILE_DIR);
            } catch (SftpException e) {
                // 如果目录不存在，则创建它
                System.out.println("远程目录不存在，正在创建: " + TARGET_FILE_DIR);
                // 创建远程目录（包括父目录，相对路径）
                createRemoteDirectory(channelSftp);
            }

            // 使用 BufferedInputStream 读取本地文件并上传到 SFTP 服务器
            String filePath =  RIGINAL_FILE_DIR + File.separator + RIGINAL_FILE;
            File local = new File(filePath);
            if (!local.exists() || !local.isFile()) {
                System.err.println("本地文件不存在或不是有效文件: " + filePath);
                return;
            }

            long fileSize = local.length();
            System.out.println("本地文件大小: " + formatFileSize(fileSize));

            try (InputStream inputStream = new FileInputStream(local);
                 BufferedInputStream bufferedIn = new BufferedInputStream(inputStream)) {
                System.out.println("开始上传文件...");
                // 上传文件
                channelSftp.put(bufferedIn, TARGET_FILE, new ProgressMonitor(fileSize), ChannelSftp.OVERWRITE);
                String remoteFilePath = TARGET_FILE_DIR + (TARGET_FILE_DIR.endsWith("/") ? "" : File.separator) + TARGET_FILE;
                System.out.println("文件上传完成: " + remoteFilePath);
            } catch (SftpException e) {
                System.err.println("SFTP 操作失败: " + e.getMessage());
                throw e;
            } catch (IOException e) {
                System.err.println("文件操作失败: " + e.getMessage());
                throw e;
            }
        } catch (Exception e) {
            System.err.println("SFTP 连接失败: " + e.getMessage());
            throw new RuntimeException(e);
        } finally {
            // 关闭连接
            if (channelSftp != null && channelSftp.isConnected()) {
                channelSftp.disconnect();
                System.out.println("已关闭 SFTP 通道");
            }
            if (session != null && session.isConnected()) {
                session.disconnect();
                System.out.println("已断开 SFTP 服务器连接");
            }
        }
    }

    private static void createRemoteDirectory(ChannelSftp channelSftp) {
        String[] folders = TARGET_FILE_DIR.split("/");
        String currentPath = "";
        for (String folder : folders) {
            if (folder.isEmpty()) {
                continue;
            }
            if (ObjectUtils.isNotEmpty(currentPath)) {
                currentPath += "/";
            }
            currentPath += folder;
            try {
                channelSftp.mkdir(currentPath);
                System.out.println("创建目录: " + currentPath);
            } catch (SftpException e) {
                // 目录可能已存在，忽略这个错误
                if (e.id != ChannelSftp.SSH_FX_FAILURE) {
                    System.err.println("无法创建目录 " + currentPath + ": " + e.getMessage());
                }
            }
        }
        try {
            channelSftp.cd(TARGET_FILE_DIR);
            System.out.println("已切换到目录: " + TARGET_FILE_DIR);
        } catch (SftpException e) {
            System.err.println("无法切换到目录 " + TARGET_FILE_DIR + ": " + e.getMessage());
        }
    }

    // 格式化文件大小显示
    private static String formatFileSize(long size) {
        if (size < 1024) {
            return size + " B";
        } else if (size < 1024 * 1024) {
            return String.format("%.2f KB", size / 1024.0);
        } else if (size < 1024 * 1024 * 1024) {
            return String.format("%.2f MB", size / (1024.0 * 1024.0));
        } else {
            return String.format("%.2f GB", size / (1024.0 * 1024.0 * 1024.0));
        }
    }

    // 进度监视器类
    private static class ProgressMonitor implements SftpProgressMonitor {
        private final long totalSize;
        private long transferred;
        private long lastReported;

        public ProgressMonitor(long totalSize) {
            this.totalSize = totalSize;
            this.transferred = 0;
            this.lastReported = 0;
        }

        @Override
        public void init(int op, String src, String dest, long max) {
            System.out.println("开始上传: " + src + " -> " + dest);
        }

        @Override
        public boolean count(long count) {
            transferred += count;

            // 每上传1MB或完成时报告进度
            if (transferred - lastReported > 1024 * 1024 || transferred == totalSize) {
                double progress = (double) transferred / totalSize * 100;
                System.out.printf("上传进度: %.2f%% (%s/%s)%n",
                        progress,
                        formatFileSize(transferred),
                        formatFileSize(totalSize));
                lastReported = transferred;
            }

            return true; // 继续传输
        }

        @Override
        public void end() {
            System.out.println("上传完成: " + formatFileSize(transferred) + " 已传输");
        }
    }
}